10 进程数据结构
无论是进程还是线程,内核里统一都叫任务(Task),由一个统一的结构 task_struct 进行管理。
内核使用链表将所有的 task_struct 串起来。
任务 ID
每个任务都有一个 ID 作为这个任务的唯一标识。
pid_t pid; # process ID
pid_t tgid; # thread group ID
struct task_struct *group_leader;
任务展示
给任务下发指令
进程如果只有主线程,pid,tgid,group_leader 都指向自己。如果创建了其他线程,tgid 是进程的主线程的 pid,其他线程有自己的 pid,group_leader 指向进程的主线程。
信号处理
- 被阻塞暂不处理(blocked)
- 尚等待处理(pending)
- 正在通过信号处理函数进行处理(sighand)
处理的结果有:忽略,结束进程等。下发信号时要区分进程和线程。
任务状态
TASK_RUNNING 表示进程在时刻准备运行的状态,获得时间片的时候后进入运行状态,如果没有获得时间片再次分配时间片,运行中的进程进行 I/O 操作,需要等待 I/O 完毕,这时会释放 CPU 进入睡眠状态。
TASK_INTERRUPTIBLE,可中断的睡眠状态。一种浅睡眠状态,等待 I/O 完成时来一个信号,进程会被唤醒,不是继续刚才的操作,而是进行信号处理。
TASK_UNINTERRUPTIBLE,不可中断的睡眠状态。一种深度睡眠状态,不可被信号唤醒,只能死等 I/O 操作完成。一旦 I/O 操作因为特殊原因不能完成,则无法再叫醒,除非重启。
进程调度
进程的状态切换,包含是否在运行队列,优先级,调度策略,可以使用哪些 CPU 等信息。
运行统计信息
进程在用户态和内核态消耗的时间、上下文切换的次数等。
进程亲缘关系
- parent 指向其父进程。当它终止时,必须向它的父进程发送信号。
- children 表示链表的头部。链表中的所有元素都是它的子进程。
- sibling 用于把当前进程插入到兄弟链表中。
进程权限
- uid 和 gid:谁启动的进程,就是谁的 ID
- euid 和 egid:当进程要操作消息队列、共享内存、信号量等对象时,比较这个用户和组是否有权限
- fsuid 和 fsgid:对文件操作审核的权限
内存管理
每个进程都有自己独立的虚拟内存空间
文件与文件系统
每个进程有一个文件系统的数据结构,还有一个打开文件的数据结构
用户态函数栈
进程的内存空间里,栈从高地址到低地址,往下增长的结构,上面是栈 底,下面是栈顶,入栈和出栈的操作是从下面的栈顶开始。
内核态函数栈
通过 task_struct 找内核栈
先从 task_struct 找到内核栈的开始位置,然后这个位置加上 THREAD_SIZE 到最后位置,然后转换为 struct pt_regs 再减一,减少一个 pt_regs 的位置,到这个结构的首地址。
通过内核栈找 task_struct
thread_info 结构有个成员变量 task 指向 task_struct,用 current_thread_info()->task 来获取 task_struct。thread_info 的位置就是内核栈的最高位置,减去 THREAD_SIZE 就到了 thread_info 的起始地址。